package main

import (
	"context"
	"flag"
	"fmt"
	"gitee.com/aqchain/backend/client"
	"gitee.com/aqchain/backend/config"
	"gitee.com/aqchain/backend/handlers"
	"gitee.com/aqchain/backend/models"
	"gitee.com/aqchain/backend/utils"
	jwt "github.com/appleboy/gin-jwt/v2"
	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
	"github.com/mitchellh/mapstructure"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"time"
)

func main() {
	wd, err := os.Getwd()
	if err != nil {
		utils.Fatal(err)
	}
	cfgPath := flag.String("configPath", path.Join(wd, "config.json"), "配置文件路径")
	flag.Parse()

	// 读取配置
	cfg, err := config.New(*cfgPath)
	if err != nil {
		utils.Fatal(err)
	}

	// 创建文件保存目录
	itemDir := filepath.Join(wd, cfg.TempDir, "item")
	_, err = os.Stat(itemDir)
	if err != nil {
		os.MkdirAll(itemDir, 0777)
	}
	certificationDir := filepath.Join(wd, cfg.TempDir, "certification")
	_, err = os.Stat(certificationDir)
	if err != nil {
		os.MkdirAll(certificationDir, 0777)
	}

	// 数据库gorm配置
	newLogger := logger.New(
		log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
		logger.Config{
			SlowThreshold: time.Second, // 慢 SQL 阈值
			LogLevel:      logger.Info, // Log level
			Colorful:      false,       // 禁用彩色打印
		},
	)
	db, err := gorm.Open(mysql.Open(cfg.DSN), &gorm.Config{
		Logger: newLogger,
	})
	if err != nil {
		utils.Fatal(err)
	}
	// 自动创建数据库表
	err = db.AutoMigrate(&models.User{}, &models.Role{}, &models.Item{}, &models.Trade{}, &models.Block{}, &models.Transaction{})
	if err != nil {
		utils.Fatal(err)
	}

	// 区块链节点连接
	ctx := context.Background()
	client, err := client.New(ctx, cfg, db)
	if err != nil {
		utils.Fatal(err)
	}

	// gin配置
	handler := handlers.NewHandler(cfg, db, client)
	authMiddleware := NewAuthMiddleware(db)

	r := gin.Default()
	r.Use(gin.Logger())
	r.Use(cors.Default())

	api := r.Group("/api")
	api.POST("/login", authMiddleware.LoginHandler)
	api.GET("/refresh", authMiddleware.RefreshHandler)
	api.POST("/register", handler.Register)

	api.POST("/alipayReturn", func(c *gin.Context) {
		log.Println("test")
	})
	api.GET("/getFile", handler.GetFile)

	api.Use(authMiddleware.MiddlewareFunc())
	{
		api.GET("/currentUser", handler.CurrentUser)
		api.POST("/addAccount", handler.AddAccount)
		api.GET("/getUser", handler.GetUser)
		api.GET("/getCertificate", handler.GetCertificate)

		api.GET("/searchItemList", handler.SearchItemList)
		api.POST("/addItem", handler.AddItem)
		api.GET("/getItem", handler.GetItem)
		api.GET("/searchTradeList", handler.SearchTradeList)
		api.POST("/addTrade", handler.AddTrade)

		api.GET("/searchAuthTradeList", handler.SearchAuthTradeList)
		api.GET("/searchSaleTradeList", handler.SearchSaleTradeList)
	}

	r.Run(fmt.Sprintf("%s:%s", cfg.Host, cfg.Port))
}

func NewAuthMiddleware(db *gorm.DB) *jwt.GinJWTMiddleware {
	var user models.User

	authMiddleware, err := jwt.New(&jwt.GinJWTMiddleware{
		Realm:      "test",
		Key:        []byte("secure key"),
		Timeout:    time.Minute * 100,
		MaxRefresh: time.Minute * 100,
		Authenticator: func(c *gin.Context) (interface{}, error) {
			mail := c.PostForm("mail")
			password := c.PostForm("password")
			if mail == "" || password == "" {
				return nil, jwt.ErrFailedAuthentication
			}
			db.Preload("Role").Where(&models.User{Mail: mail}).First(&user)
			if &user != nil {
				err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password))
				if err == nil {
					return &user, nil
				}
			}
			return nil, jwt.ErrFailedAuthentication
		},
		Authorizator: func(data interface{}, c *gin.Context) bool {
			_, ok := data.(*models.User)
			if ok {
				return true
			}
			return false
		},
		PayloadFunc: func(data interface{}) jwt.MapClaims {
			if v, ok := data.(jwt.MapClaims); ok {
				return v
			}
			user, ok := data.(*models.User)
			if !ok {
				return jwt.MapClaims{}
			}
			return jwt.MapClaims{jwt.IdentityKey: user, "userID": user.ID}
		},
		Unauthorized: func(c *gin.Context, code int, message string) {
			c.JSON(code, gin.H{
				"success": false,
				"message": message,
			})
		},
		IdentityHandler: func(c *gin.Context) interface{} {
			var user models.User
			claims := jwt.ExtractClaims(c)
			err := mapstructure.Decode(claims[jwt.IdentityKey], &user)
			if err != nil {
				return nil
			}
			// todo 验证user的部分内容
			return &user
		},
		TimeFunc: time.Now,
		LoginResponse: func(c *gin.Context, code int, token string, t time.Time) {
			c.JSON(http.StatusOK, gin.H{
				"success":          true,
				"token":            token,
				"currentAuthority": user.Role.Label,
			})
			user = models.User{}
		},
	})

	if err != nil {
		utils.Fatal(err)
	}
	return authMiddleware
}
